home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 7 / Amiga Format AFCD07 (Dec 1996, Issue 91).iso / serious / shareware / programming / emacs-complete / fsf / emacs / src / indent.c < prev    next >
C/C++ Source or Header  |  1994-08-27  |  27KB  |  898 lines

  1. /* Indentation functions.
  2.    Copyright (C) 1985,86,87,88,93,94 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU Emacs.
  5.  
  6. GNU Emacs is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Emacs is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Emacs; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20.  
  21. #include <config.h>
  22. #include "lisp.h"
  23. #include "buffer.h"
  24. #include "indent.h"
  25. #include "frame.h"
  26. #include "window.h"
  27. #include "termchar.h"
  28. #include "termopts.h"
  29. #include "disptab.h"
  30. #include "intervals.h"
  31.  
  32. /* Indentation can insert tabs if this is non-zero;
  33.    otherwise always uses spaces */
  34. int indent_tabs_mode;
  35.  
  36. #define min(a, b) ((a) < (b) ? (a) : (b))
  37. #define max(a, b) ((a) > (b) ? (a) : (b))
  38.  
  39. #define CR 015
  40.  
  41. /* These three values memoize the current column to avoid recalculation */
  42. /* Some things in set last_known_column_point to -1
  43.   to mark the memoized value as invalid */
  44. /* Last value returned by current_column */
  45. int last_known_column;
  46. /* Value of point when current_column was called */
  47. int last_known_column_point;
  48. /* Value of MODIFF when current_column was called */
  49. int last_known_column_modified;
  50.  
  51. /* Get the display table to use for the current buffer.  */
  52.  
  53. struct Lisp_Vector *
  54. buffer_display_table ()
  55. {
  56.   Lisp_Object thisbuf;
  57.  
  58.   thisbuf = current_buffer->display_table;
  59.   if (XTYPE (thisbuf) == Lisp_Vector
  60.       && XVECTOR (thisbuf)->size == DISP_TABLE_SIZE)
  61.     return XVECTOR (thisbuf);
  62.   if (XTYPE (Vstandard_display_table) == Lisp_Vector
  63.       && XVECTOR (Vstandard_display_table)->size == DISP_TABLE_SIZE)
  64.     return XVECTOR (Vstandard_display_table);
  65.   return 0;
  66. }
  67.  
  68. DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0,
  69.   "Return the horizontal position of point.  Beginning of line is column 0.\n\
  70. This is calculated by adding together the widths of all the displayed\n\
  71. representations of the character between the start of the previous line\n\
  72. and point.  (eg control characters will have a width of 2 or 4, tabs\n\
  73. will have a variable width)\n\
  74. Ignores finite width of frame, which means that this function may return\n\
  75. values greater than (frame-width).\n\
  76. Whether the line is visible (if `selective-display' is t) has no effect;\n\
  77. however, ^M is treated as end of line when `selective-display' is t.")
  78.   ()
  79. {
  80.   Lisp_Object temp;
  81.   XFASTINT (temp) = current_column ();
  82.   return temp;
  83. }
  84.  
  85. /* Cancel any recorded value of the horizontal position.  */
  86.  
  87. invalidate_current_column ()
  88. {
  89.   last_known_column_point = 0;
  90. }
  91.  
  92. int
  93. current_column ()
  94. {
  95.   register int col;
  96.   register unsigned char *ptr, *stop;
  97.   register int tab_seen;
  98.   int post_tab;
  99.   register int c;
  100.   register int tab_width = XINT (current_buffer->tab_width);
  101.   int ctl_arrow = !NILP (current_buffer->ctl_arrow);
  102.   register struct Lisp_Vector *dp = buffer_display_table ();
  103.   int stopchar;
  104.  
  105.   if (point == last_known_column_point
  106.       && MODIFF == last_known_column_modified)
  107.     return last_known_column;
  108.  
  109.   /* Make a pointer for decrementing through the chars before point.  */
  110.   ptr = &FETCH_CHAR (point - 1) + 1;
  111.   /* Make a pointer to where consecutive chars leave off,
  112.      going backwards from point.  */
  113.   if (point == BEGV)
  114.     stop = ptr;
  115.   else if (point <= GPT || BEGV > GPT)
  116.     stop = BEGV_ADDR;
  117.   else
  118.     stop = GAP_END_ADDR;
  119.  
  120.   if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
  121.  
  122.   col = 0, tab_seen = 0, post_tab = 0;
  123.  
  124.   while (1)
  125.     {
  126.       if (ptr == stop)
  127.     {
  128.       /* We stopped either for the beginning of the buffer
  129.          or for the gap.  */
  130.       if (ptr == BEGV_ADDR)
  131.         break;
  132.       /* It was the gap.  Jump back over it.  */
  133.       stop = BEGV_ADDR;
  134.       ptr = GPT_ADDR;
  135.       /* Check whether that brings us to beginning of buffer.  */
  136.       if (BEGV >= GPT) break;
  137.     }
  138.  
  139.       c = *--ptr;
  140.       if (c >= 040 && c < 0177
  141.       && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
  142.     {
  143.       col++;
  144.     }
  145.       else if (c == '\n')
  146.     break;
  147.       else if (c == '\r' && EQ (current_buffer->selective_display, Qt))
  148.     break;
  149.       else if (c == '\t')
  150.     {
  151.       if (tab_seen)
  152.         col = ((col + tab_width) / tab_width) * tab_width;
  153.  
  154.       post_tab += col;
  155.       col = 0;
  156.       tab_seen = 1;
  157.     }
  158.       else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
  159.     col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
  160.       else
  161.     col += (ctl_arrow && c < 0200) ? 2 : 4;
  162.     }
  163.  
  164.   if (tab_seen)
  165.     {
  166.       col = ((col + tab_width) / tab_width) * tab_width;
  167.       col += post_tab;
  168.     }
  169.  
  170.   last_known_column = col;
  171.   last_known_column_point = point;
  172.   last_known_column_modified = MODIFF;
  173.  
  174.   return col;
  175. }
  176.  
  177.  
  178. DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ",
  179.   "Indent from point with tabs and spaces until COLUMN is reached.\n\
  180. Optional second argument MIN says always do at least MIN spaces\n\
  181. even if that goes past COLUMN; by default, MIN is zero.")
  182.   (col, minimum)
  183.      Lisp_Object col, minimum;
  184. {
  185.   int mincol;
  186.   register int fromcol;
  187.   register int tab_width = XINT (current_buffer->tab_width);
  188.  
  189.   CHECK_NUMBER (col, 0);
  190.   if (NILP (minimum))
  191.     XFASTINT (minimum) = 0;
  192.   CHECK_NUMBER (minimum, 1);
  193.  
  194.   fromcol = current_column ();
  195.   mincol = fromcol + XINT (minimum);
  196.   if (mincol < XINT (col)) mincol = XINT (col);
  197.  
  198.   if (fromcol == mincol)
  199.     return make_number (mincol);
  200.  
  201.   if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
  202.  
  203.   if (indent_tabs_mode)
  204.     {
  205.       Lisp_Object n;
  206.       XFASTINT (n) = mincol / tab_width - fromcol / tab_width;
  207.       if (XFASTINT (n) != 0)
  208.     {
  209.       Finsert_char (make_number ('\t'), n, Qt);
  210.  
  211.       fromcol = (mincol / tab_width) * tab_width;
  212.     }
  213.     }
  214.  
  215.   XFASTINT (col) = mincol - fromcol;
  216.   Finsert_char (make_number (' '), col, Qt);
  217.  
  218.   last_known_column = mincol;
  219.   last_known_column_point = point;
  220.   last_known_column_modified = MODIFF;
  221.  
  222.   XSETINT (col, mincol);
  223.   return col;
  224. }
  225.  
  226. DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation,
  227.   0, 0, 0,
  228.   "Return the indentation of the current line.\n\
  229. This is the horizontal position of the character\n\
  230. following any initial whitespace.")
  231.   ()
  232. {
  233.   Lisp_Object val;
  234.  
  235.   XFASTINT (val) = position_indentation (find_next_newline (point, -1));
  236.   return val;
  237. }
  238.  
  239. position_indentation (pos)
  240.      register int pos;
  241. {
  242.   register int column = 0;
  243.   register int tab_width = XINT (current_buffer->tab_width);
  244.   register unsigned char *p;
  245.   register unsigned char *stop;
  246.   
  247.   if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
  248.   
  249.   stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
  250.   p = &FETCH_CHAR (pos);
  251.   while (1)
  252.     {
  253.       while (p == stop)
  254.     {
  255.       if (pos == ZV)
  256.         return column;
  257.       pos += p - &FETCH_CHAR (pos);
  258.       p = &FETCH_CHAR (pos);
  259.       stop = &FETCH_CHAR (BUFFER_CEILING_OF (pos)) + 1;
  260.     }
  261.       switch (*p++)
  262.     {
  263.     case ' ':
  264.       column++;
  265.       break;
  266.     case '\t':
  267.       column += tab_width - column % tab_width;
  268.       break;
  269.     default:
  270.       return column;
  271.     }
  272.     }
  273. }
  274.  
  275. /* Test whether the line beginning at POS is indented beyond COLUMN.
  276.    Blank lines are treated as if they had the same indentation as the
  277.    preceding line.  */
  278. int
  279. indented_beyond_p (pos, column)
  280.      int pos, column;
  281. {
  282.   while (pos > BEGV && FETCH_CHAR (pos) == '\n')
  283.     pos = find_next_newline_no_quit (pos - 1, -1);
  284.   return (position_indentation (pos) >= column);
  285. }
  286.  
  287. DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, 0,
  288.   "Move point to column COLUMN in the current line.\n\
  289. The column of a character is calculated by adding together the widths\n\
  290. as displayed of the previous characters in the line.\n\
  291. This function ignores line-continuation;\n\
  292. there is no upper limit on the column number a character can have\n\
  293. and horizontal scrolling has no effect.\n\
  294. \n\
  295. If specified column is within a character, point goes after that character.\n\
  296. If it's past end of line, point goes to end of line.\n\n\
  297. A non-nil second (optional) argument FORCE means, if the line\n\
  298. is too short to reach column COLUMN then add spaces/tabs to get there,\n\
  299. and if COLUMN is in the middle of a tab character, change it to spaces.")
  300.   (column, force)
  301.      Lisp_Object column, force;
  302. {
  303.   register int pos;
  304.   register int col = current_column ();
  305.   register int goal;
  306.   register int end;
  307.   register int tab_width = XINT (current_buffer->tab_width);
  308.   register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
  309.   register struct Lisp_Vector *dp = buffer_display_table ();
  310.  
  311.   Lisp_Object val;
  312.   int prev_col;
  313.   int c;
  314.  
  315.   if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
  316.   CHECK_NATNUM (column, 0);
  317.   goal = XINT (column);
  318.  
  319.  retry:
  320.   pos = point;
  321.   end = ZV;
  322.  
  323.   /* If we're starting past the desired column,
  324.      back up to beginning of line and scan from there.  */
  325.   if (col > goal)
  326.     {
  327.       pos = find_next_newline (pos, -1);
  328.       col = 0;
  329.     }
  330.  
  331.   while (col < goal && pos < end)
  332.     {
  333.       c = FETCH_CHAR (pos);
  334.       if (c == '\n')
  335.     break;
  336.       if (c == '\r' && EQ (current_buffer->selective_display, Qt))
  337.     break;
  338.       pos++;
  339.       if (c == '\t')
  340.     {
  341.       prev_col = col;
  342.       col += tab_width;
  343.       col = col / tab_width * tab_width;
  344.     }
  345.       else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
  346.     col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
  347.       else if (ctl_arrow && (c < 040 || c == 0177))
  348.         col += 2;
  349.       else if (c < 040 || c >= 0177)
  350.         col += 4;
  351.       else
  352.     col++;
  353.     }
  354.  
  355.   SET_PT (pos);
  356.  
  357.   /* If a tab char made us overshoot, change it to spaces
  358.      and scan through it again.  */
  359.   if (!NILP (force) && col > goal && c == '\t' && prev_col < goal)
  360.     {
  361.       int old_point;
  362.  
  363.       del_range (point - 1, point);
  364.       Findent_to (make_number (goal), Qnil);
  365.       old_point = point;
  366.       Findent_to (make_number (col), Qnil);
  367.       SET_PT (old_point);
  368.       /* Set the last_known... vars consistently.  */
  369.       col = goal;
  370.     }
  371.  
  372.   /* If line ends prematurely, add space to the end.  */
  373.   if (col < goal && !NILP (force))
  374.     Findent_to (make_number (col = goal), Qnil);
  375.  
  376.   last_known_column = col;
  377.   last_known_column_point = point;
  378.   last_known_column_modified = MODIFF;
  379.  
  380.   XFASTINT (val) = col;
  381.   return val;
  382. }
  383.  
  384. struct position val_compute_motion;
  385.  
  386. /* Scan the current buffer forward from offset FROM, pretending that
  387.    this is at line FROMVPOS, column FROMHPOS, until reaching buffer
  388.    offset TO or line TOVPOS, column TOHPOS (whichever comes first),
  389.    and return the ending buffer position and screen location.
  390.  
  391.    WIDTH is the number of columns available to display text;
  392.    compute_motion uses this to handle continuation lines and such.
  393.    HSCROLL is the number of columns not being displayed at the left
  394.    margin; this is usually taken from a window's hscroll member.
  395.    TAB_OFFSET is the number of columns of the first tab that aren't
  396.    being displayed, perhaps because of a continuation line or
  397.    something.
  398.  
  399.    compute_motion returns a pointer to a struct position.  The bufpos
  400.    member gives the buffer position at the end of the scan, and hpos
  401.    and vpos give its cartesian location.  I'm not clear on what the
  402.    other members are.
  403.  
  404.    Note that FROMHPOS and TOHPOS should be expressed in real screen
  405.    columns, taking HSCROLL and the truncation glyph at the left margin
  406.    into account.  That is, beginning-of-line moves you to the hpos
  407.    -HSCROLL + (HSCROLL > 0).
  408.  
  409.    For example, to find the buffer position of column COL of line LINE
  410.    of a certain window, pass the window's starting location as FROM
  411.    and the window's upper-left coordinates as FROMVPOS and FROMHPOS.
  412.    Pass the buffer's ZV as TO, to limit the scan to the end of the
  413.    visible section of the buffer, and pass LINE and COL as TOVPOS and
  414.    TOHPOS.  
  415.  
  416.    When displaying in window w, a typical formula for WIDTH is:
  417.  
  418.     window_width - 1
  419.      - (has_vertical_scroll_bars
  420.         ? VERTICAL_SCROLL_BAR_WIDTH
  421.         : (window_width + window_left != frame_width))
  422.  
  423.     where
  424.       window_width is XFASTINT (w->width),
  425.       window_left is XFASTINT (w->left),
  426.       has_vertical_scroll_bars is
  427.         FRAME_HAS_VERTICAL_SCROLL_BARS (XFRAME (WINDOW_FRAME (window)))
  428.       and frame_width = FRAME_WIDTH (XFRAME (window->frame))
  429.  
  430.    Or you can let window_internal_width do this all for you, and write:
  431.     window_internal_width (w) - 1
  432.  
  433.    The `-1' accounts for the continuation-line backslashes; the rest
  434.    accounts for window borders if the window is split horizontally, and
  435.    the scroll bars if they are turned on.  */
  436.  
  437. struct position *
  438. compute_motion (from, fromvpos, fromhpos, to, tovpos, tohpos, width, hscroll, tab_offset, win)
  439.      int from, fromvpos, fromhpos, to, tovpos, tohpos;
  440.      register int width;
  441.      int hscroll, tab_offset;
  442.      struct window *win;
  443. {
  444.   register int hpos = fromhpos;
  445.   register int vpos = fromvpos;
  446.  
  447.   register int pos;
  448.   register int c;
  449.   register int tab_width = XFASTINT (current_buffer->tab_width);
  450.   register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
  451.   register struct Lisp_Vector *dp = window_display_table (win);
  452.   int selective
  453.     = (XTYPE (current_buffer->selective_display) == Lisp_Int
  454.        ? XINT (current_buffer->selective_display)
  455.        : !NILP (current_buffer->selective_display) ? -1 : 0);
  456.   int prev_vpos, prev_hpos = 0;
  457.   int selective_rlen
  458.     = (selective && dp && XTYPE (DISP_INVIS_VECTOR (dp)) == Lisp_Vector
  459.        ? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
  460. #ifdef USE_TEXT_PROPERTIES
  461.   /* The next location where the `invisible' property changes */
  462.   int next_invisible = from;
  463.   Lisp_Object prop, position;
  464. #endif
  465.  
  466.   if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
  467.   for (pos = from; pos < to; pos++)
  468.     {
  469.       /* Stop if past the target screen position.  */
  470.       if (vpos > tovpos
  471.       || (vpos == tovpos && hpos >= tohpos))
  472.     break;
  473.  
  474.       prev_vpos = vpos;
  475.       prev_hpos = hpos;
  476.  
  477. #ifdef USE_TEXT_PROPERTIES
  478.       /* if the `invisible' property is set, we can skip to
  479.      the next property change */
  480.       while (pos == next_invisible && pos < to)
  481.     {
  482.       XFASTINT (position) = pos;
  483.       prop = Fget_char_property (position,
  484.                      Qinvisible,
  485.                      Fcurrent_buffer ());
  486.       {
  487.         Lisp_Object end, limit;
  488.  
  489.         recenter_overlay_lists (current_buffer, pos);
  490.         /* This is just an estimate to give reasonable
  491.            performance; nothing should go wrong if it is too small.  */
  492.         limit = Fnext_overlay_change (position);
  493.         if (XFASTINT (limit) > pos + 100)
  494.           XFASTINT (limit) = pos + 100;
  495.         end = Fnext_single_property_change (position, Qinvisible,
  496.                         Fcurrent_buffer (), limit);
  497.         if (INTEGERP (end))
  498.           next_invisible = XINT (end);
  499.         else
  500.           next_invisible = to;
  501.         if (! NILP (prop))
  502.           pos = next_invisible;
  503.       }
  504.     }
  505.       if (pos >= to)
  506.     break;
  507. #endif
  508.       c = FETCH_CHAR (pos);
  509.       if (c >= 040 && c < 0177
  510.       && (dp == 0 || XTYPE (DISP_CHAR_VECTOR (dp, c)) != Lisp_Vector))
  511.     hpos++;
  512.       else if (c == '\t')
  513.     {
  514.       hpos += tab_width - ((hpos + tab_offset + hscroll - (hscroll > 0)
  515.                 /* Add tab_width here to make sure positive.
  516.                    hpos can be negative after continuation
  517.                    but can't be less than -tab_width.  */
  518.                 + tab_width)
  519.                    % tab_width);
  520.     }
  521.       else if (c == '\n')
  522.     {
  523.       if (selective > 0 && indented_beyond_p (pos + 1, selective))
  524.         {
  525.           /* Skip any number of invisible lines all at once */
  526.           do
  527.         {
  528.           while (++pos < to && FETCH_CHAR (pos) != '\n');
  529.         }
  530.           while (pos < to && indented_beyond_p (pos + 1, selective));
  531.           pos--;        /* Reread the newline on the next pass.  */
  532.           /* Allow for the " ..." that is displayed for them. */
  533.           if (selective_rlen)
  534.         {
  535.           hpos += selective_rlen;
  536.           if (hpos >= width)
  537.             hpos = width;
  538.         }
  539.           /* We have skipped the invis text, but not the newline after.  */
  540.         }
  541.       else
  542.         {
  543.           /* A visible line.  */
  544.           vpos++;
  545.           hpos = 0;
  546.           hpos -= hscroll;
  547.           if (hscroll > 0) hpos++; /* Truncation glyph on column 0 */
  548.           tab_offset = 0;
  549.         }
  550.     }
  551.       else if (c == CR && selective < 0)
  552.     {
  553.       /* In selective display mode,
  554.          everything from a ^M to the end of the line is invisible */
  555.       while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
  556.       /* Stop *before* the real newline.  */
  557.       pos--;
  558.       /* Allow for the " ..." that is displayed for them. */
  559.       if (selective_rlen)
  560.         {
  561.           hpos += selective_rlen;
  562.           if (hpos >= width)
  563.         hpos = width;
  564.         }
  565.     }
  566.       else if (dp != 0 && XTYPE (DISP_CHAR_VECTOR (dp, c)) == Lisp_Vector)
  567.     hpos += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
  568.       else
  569.     hpos += (ctl_arrow && c < 0200) ? 2 : 4;
  570.  
  571.       /* Handle right margin.  */
  572.       if (hpos >= width
  573.       && (hpos > width
  574.           || (pos < ZV - 1
  575.           && FETCH_CHAR (pos + 1) != '\n')))
  576.     {
  577.       if (vpos > tovpos
  578.           || (vpos == tovpos && hpos >= tohpos))
  579.         break;
  580.       if (hscroll
  581.           || (truncate_partial_width_windows
  582.           && width + 1 < FRAME_WIDTH (XFRAME (WINDOW_FRAME (win))))
  583.           || !NILP (current_buffer->truncate_lines))
  584.         {
  585.           /* Truncating: skip to newline.  */
  586.           while (pos < to && FETCH_CHAR (pos) != '\n') pos++;
  587.           pos--;
  588.           hpos = width;
  589.         }
  590.       else
  591.         {
  592.           /* Continuing.  */
  593.           vpos++;
  594.           hpos -= width;
  595.           tab_offset += width;
  596.         }
  597.  
  598.     }
  599.     }
  600.  
  601.   val_compute_motion.bufpos = pos;
  602.   val_compute_motion.hpos = hpos;
  603.   val_compute_motion.vpos = vpos;
  604.   val_compute_motion.prevhpos = prev_hpos;
  605.  
  606.   /* Nonzero if have just continued a line */
  607.   val_compute_motion.contin
  608.     = (pos != from
  609.        && (val_compute_motion.vpos != prev_vpos)
  610.        && c != '\n');
  611.  
  612.   return &val_compute_motion;
  613. }
  614.  
  615. #if 0 /* The doc string is too long for some compilers,
  616.      but make-docfile can find it in this comment.  */
  617. DEFUN ("compute-motion", Ffoo, Sfoo, 7, 7, 0,
  618.   "Scan through the current buffer, calculating screen position.\n\
  619. Scan the current buffer forward from offset FROM,\n\
  620. assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--\n\
  621. to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--\n\
  622. and return the ending buffer position and screen location.\n\
  623. \n\
  624. There are three additional arguments:\n\
  625. \n\
  626. WIDTH is the number of columns available to display text;\n\
  627. this affects handling of continuation lines.\n\
  628. This is usually the value returned by `window-width', less one (to allow\n\
  629. for the continuation glyph).\n\
  630. \n\
  631. OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).\n\
  632. HSCROLL is the number of columns not being displayed at the left\n\
  633. margin; this is usually taken from a window's hscroll member.\n\
  634. TAB-OFFSET is the number of columns of the first tab that aren't\n\
  635. being displayed, perhaps because the line was continued within it.\n\
  636. If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.\n\
  637. \n\
  638. WINDOW is the window to operate on.  Currently this is used only to\n\
  639. find the display table.  It does not matter what buffer WINDOW displays;\n\
  640. `compute-motion' always operates on the current buffer.\n\
  641. \n\
  642. The value is a list of five elements:\n\
  643.   (POS HPOS VPOS PREVHPOS CONTIN)\n\
  644. POS is the buffer position where the scan stopped.\n\
  645. VPOS is the vertical position where the scan stopped.\n\
  646. HPOS is the horizontal position where the scan stopped.\n\
  647. \n\
  648. PREVHPOS is the horizontal position one character back from POS.\n\
  649. CONTIN is t if a line was continued after (or within) the previous character.\n\
  650. \n\
  651. For example, to find the buffer position of column COL of line LINE\n\
  652. of a certain window, pass the window's starting location as FROM\n\
  653. and the window's upper-left coordinates as FROMPOS.\n\
  654. Pass the buffer's (point-max) as TO, to limit the scan to the end of the\n\
  655. visible section of the buffer, and pass LINE and COL as TOPOS.")
  656.   (from, frompos, to, topos, width, offsets, window)
  657. #endif
  658.  
  659. DEFUN ("compute-motion", Fcompute_motion, Scompute_motion, 7, 7, 0,
  660.   0)
  661.   (from, frompos, to, topos, width, offsets, window)
  662.      Lisp_Object from, frompos, to, topos;
  663.      Lisp_Object width, offsets, window;
  664. {
  665.   Lisp_Object bufpos, hpos, vpos, prevhpos, contin;
  666.   struct position *pos;
  667.   int hscroll, tab_offset;
  668.  
  669.   CHECK_NUMBER_COERCE_MARKER (from, 0);
  670.   CHECK_CONS (frompos, 0);
  671.   CHECK_NUMBER (XCONS (frompos)->car, 0);
  672.   CHECK_NUMBER (XCONS (frompos)->cdr, 0);
  673.   CHECK_NUMBER_COERCE_MARKER (to, 0);
  674.   CHECK_CONS (topos, 0);
  675.   CHECK_NUMBER (XCONS (topos)->car, 0);
  676.   CHECK_NUMBER (XCONS (topos)->cdr, 0);
  677.   CHECK_NUMBER (width, 0);
  678.   if (!NILP (offsets))
  679.     {
  680.       CHECK_CONS (offsets, 0);
  681.       CHECK_NUMBER (XCONS (offsets)->car, 0);
  682.       CHECK_NUMBER (XCONS (offsets)->cdr, 0);
  683.       hscroll = XINT (XCONS (offsets)->car);
  684.       tab_offset = XINT (XCONS (offsets)->cdr);
  685.     }
  686.   else
  687.     hscroll = tab_offset = 0;
  688.  
  689.   if (NILP (window))
  690.     window = Fselected_window ();
  691.   else
  692.     CHECK_LIVE_WINDOW (window, 0);
  693.  
  694.   pos = compute_motion (XINT (from), XINT (XCONS (frompos)->cdr),
  695.             XINT (XCONS (frompos)->car),
  696.             XINT (to), XINT (XCONS (topos)->cdr),
  697.             XINT (XCONS (topos)->car),
  698.             XINT (width), hscroll, tab_offset,
  699.             XWINDOW (window));
  700.  
  701.   XFASTINT (bufpos) = pos->bufpos;
  702.   XSET (hpos, Lisp_Int, pos->hpos);
  703.   XSET (vpos, Lisp_Int, pos->vpos);
  704.   XSET (prevhpos, Lisp_Int, pos->prevhpos);
  705.  
  706.   return Fcons (bufpos,
  707.         Fcons (hpos,
  708.                Fcons (vpos,
  709.                   Fcons (prevhpos,
  710.                      Fcons (pos->contin ? Qt : Qnil, Qnil)))));
  711.  
  712. }
  713.  
  714. /* Return the column of position POS in window W's buffer,
  715.    rounded down to a multiple of the internal width of W.
  716.    This is the amount of indentation of position POS
  717.    that is not visible in its horizontal position in the window.  */
  718.  
  719. int
  720. pos_tab_offset (w, pos)
  721.      struct window *w;
  722.      register int pos;
  723. {
  724.   int opoint = PT;
  725.   int col;
  726.   int width = window_internal_width (w) - 1;
  727.  
  728.   if (pos == BEGV || FETCH_CHAR (pos - 1) == '\n')
  729.     return 0;
  730.   TEMP_SET_PT (pos);
  731.   col = current_column ();
  732.   TEMP_SET_PT (opoint);
  733.   return col - (col % width);
  734. }
  735.  
  736. /* start_hpos is the hpos of the first character of the buffer:
  737.    zero except for the minibuffer window,
  738.    where it is the width of the prompt.  */
  739.  
  740. struct position val_vmotion;
  741.  
  742. struct position *
  743. vmotion (from, vtarget, width, hscroll, window)
  744.      register int from, vtarget, width;
  745.      int hscroll;
  746.      Lisp_Object window;
  747. {
  748.   struct position pos;
  749.   /* vpos is cumulative vertical position, changed as from is changed */
  750.   register int vpos = 0;
  751.   register int prevline;
  752.   register int first;
  753.   int lmargin = hscroll > 0 ? 1 - hscroll : 0;
  754.   int selective
  755.     = XTYPE (current_buffer->selective_display) == Lisp_Int
  756.       ? XINT (current_buffer->selective_display)
  757.     : !NILP (current_buffer->selective_display) ? -1 : 0;
  758.   /* The omission of the clause
  759.          && marker_position (XWINDOW (window)->start) == BEG
  760.      here is deliberate; I think we want to measure from the prompt
  761.      position even if the minibuffer window has scrolled.  */
  762.   int start_hpos = (EQ (window, minibuf_window) ? minibuf_prompt_width : 0);
  763.  
  764.  retry:
  765.   if (vtarget > vpos)
  766.     {
  767.       /* Moving downward is simple, but must calculate from beg of line 
  768.      to determine hpos of starting point */
  769.       if (from > BEGV && FETCH_CHAR (from - 1) != '\n')
  770.     {
  771.       prevline = find_next_newline_no_quit (from, -1);
  772.       while (prevline > BEGV
  773.          && ((selective > 0
  774.               && indented_beyond_p (prevline, selective))
  775. #ifdef USE_TEXT_PROPERTIES
  776.              /* watch out for newlines with `invisible' property */
  777.              || ! NILP (Fget_char_property (XFASTINT (prevline),
  778.                             Qinvisible,
  779.                             window))
  780. #endif
  781.          ))
  782.         prevline = find_next_newline_no_quit (prevline - 1, -1);
  783.       pos = *compute_motion (prevline, 0,
  784.                  lmargin + (prevline == 1 ? start_hpos : 0),
  785.                  from, 1 << (INTBITS - 2), 0,
  786.                  width, hscroll, 0, XWINDOW (window));
  787.     }
  788.       else
  789.     {
  790.       pos.hpos = lmargin + (from == 1 ? start_hpos : 0);
  791.       pos.vpos = 0;
  792.     }
  793.       return compute_motion (from, vpos, pos.hpos,
  794.                  ZV, vtarget, - (1 << (INTBITS - 2)),
  795.                  width, hscroll, pos.vpos * width,
  796.                  XWINDOW (window));
  797.     }
  798.  
  799.   /* To move upward, go a line at a time until
  800.      we have gone at least far enough */
  801.  
  802.   first = 1;
  803.  
  804.   while ((vpos > vtarget || first) && from > BEGV)
  805.     {
  806.       prevline = from;
  807.       while (1)
  808.     {
  809.       prevline = find_next_newline_no_quit (prevline - 1, -1);
  810.       if (prevline == BEGV
  811.           || ((selective <= 0
  812.            || ! indented_beyond_p (prevline, selective))
  813. #ifdef USE_TEXT_PROPERTIES
  814.           /* watch out for newlines with `invisible' property */
  815.           && NILP (Fget_char_property (XFASTINT (prevline),
  816.                            Qinvisible,
  817.                            window))
  818. #endif
  819.           ))
  820.         break;
  821.     }
  822.       pos = *compute_motion (prevline, 0,
  823.                  lmargin + (prevline == 1 ? start_hpos : 0),
  824.                  from, 1 << (INTBITS - 2), 0,
  825.                  width, hscroll, 0, XWINDOW (window));
  826.       vpos -= pos.vpos;
  827.       first = 0;
  828.       from = prevline;
  829.     }
  830.  
  831.   /* If we made exactly the desired vertical distance,
  832.      or if we hit beginning of buffer,
  833.      return point found */
  834.   if (vpos >= vtarget)
  835.     {
  836.       val_vmotion.bufpos = from;
  837.       val_vmotion.vpos = vpos;
  838.       val_vmotion.hpos = lmargin;
  839.       val_vmotion.contin = 0;
  840.       val_vmotion.prevhpos = 0;
  841.       return &val_vmotion;
  842.     }
  843.   
  844.   /* Otherwise find the correct spot by moving down */
  845.   goto retry;
  846. }
  847.  
  848. DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 2, 0,
  849.   "Move to start of screen line LINES lines down.\n\
  850. If LINES is negative, this is moving up.\n\
  851. \n\
  852. The optional second argument WINDOW specifies the window to use for\n\
  853. parameters such as width, horizontal scrolling, and so on.\n\
  854. the default is the selected window.\n\
  855. It does not matter what buffer is displayed in WINDOW.\n\
  856. `vertical-motion' always uses the current buffer.\n\
  857. \n\
  858. Sets point to position found; this may be start of line\n\
  859. or just the start of a continuation line.\n\
  860. Returns number of lines moved; may be closer to zero than LINES\n\
  861. if beginning or end of buffer was reached.")
  862.   (lines, window)
  863.      Lisp_Object lines, window;
  864. {
  865.   struct position pos;
  866.   register struct window *w;
  867.  
  868.   CHECK_NUMBER (lines, 0);
  869.   if (! NILP (window))
  870.     CHECK_WINDOW (window, 0);
  871.   else
  872.     XSET (window, Lisp_Window, selected_window);
  873.  
  874.   w = XWINDOW (window);
  875.  
  876.   pos = *vmotion (point, XINT (lines), window_internal_width (w) - 1,
  877.           /* Not XFASTINT since perhaps could be negative */
  878.           XINT (w->hscroll), window);
  879.  
  880.   SET_PT (pos.bufpos);
  881.   return make_number (pos.vpos);
  882. }
  883.  
  884. syms_of_indent ()
  885. {
  886.   DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
  887.     "*Indentation can insert tabs if this is non-nil.\n\
  888. Setting this variable automatically makes it local to the current buffer.");
  889.   indent_tabs_mode = 1;
  890.  
  891.   defsubr (&Scurrent_indentation);
  892.   defsubr (&Sindent_to);
  893.   defsubr (&Scurrent_column);
  894.   defsubr (&Smove_to_column);
  895.   defsubr (&Svertical_motion);
  896.   defsubr (&Scompute_motion);
  897. }
  898.